win32: resurect Windows clipboard selection notification
authorMarc-André Lureau <marcandre.lureau@redhat.com>
Thu, 9 Jun 2011 18:45:20 +0000 (20:45 +0200)
committerAlexander Larsson <alexl@redhat.com>
Thu, 10 Nov 2011 16:40:48 +0000 (17:40 +0100)
This is a rewrite of e6fa7394baa8a7cb80ae01a0c81729717019172b, with
misc fixes that should help with some bugs Tim was talking about.

https://bugzilla.gnome.org/show_bug.cgi?id=652239

gdk/win32/gdkdisplay-win32.c
gdk/win32/gdkevents-win32.c
gdk/win32/gdkprivate-win32.h

index 8e7c9881b149371f28ca751c0fc67c2381093868..7c783d399180e87eb3d6a7b0830c30fd611e7f46 100644 (file)
@@ -79,7 +79,7 @@ enum_monitor (HMONITOR hmonitor,
     DWORD dwFlags;
     CHAR szDevice[CCHDEVICENAME];
   } MONITORINFOEXA2;
-  
+
   MONITORINFOEXA2 monitor_info;
   HDC hDC;
 
@@ -252,7 +252,7 @@ gdk_win32_display_get_name (GdkDisplay *display)
   PFN_ProcessIdToSessionId processIdToSessionId;
 
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
-  
+
   if (display_name_cache != NULL)
     return display_name_cache;
 
@@ -304,7 +304,7 @@ static gint
 gdk_win32_display_get_n_screens (GdkDisplay *display)
 {
   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
-  
+
   return 1;
 }
 
@@ -341,15 +341,189 @@ gdk_win32_display_supports_selection_notification (GdkDisplay *display)
 {
   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
 
-  return FALSE;
+  return TRUE;
+}
+
+static HWND _hwnd_next_viewer = NULL;
+static int debug_indent = 0;
+
+/*
+ * maybe this should be integrated with the default message loop - or maybe not ;-)
+ */
+static LRESULT CALLBACK
+inner_clipboard_window_procedure (HWND   hwnd,
+                                  UINT   message,
+                                  WPARAM wparam,
+                                  LPARAM lparam)
+{
+  switch (message)
+    {
+    case WM_DESTROY: /* remove us from chain */
+      {
+        ChangeClipboardChain (hwnd, _hwnd_next_viewer);
+        PostQuitMessage (0);
+        return 0;
+      }
+    case WM_CHANGECBCHAIN:
+      {
+        HWND hwndRemove = (HWND) wparam; /* handle of window being removed */
+        HWND hwndNext   = (HWND) lparam; /* handle of next window in chain */
+
+        if (hwndRemove == _hwnd_next_viewer)
+          _hwnd_next_viewer = hwndNext == hwnd ? NULL : hwndNext;
+        else if (_hwnd_next_viewer != NULL)
+          return SendMessage (_hwnd_next_viewer, message, wparam, lparam);
+
+        return 0;
+      }
+#ifdef WM_CLIPBOARDUPDATE
+    case WM_CLIPBOARDUPDATE:
+#endif
+    case WM_DRAWCLIPBOARD:
+      {
+        int success;
+        HWND hwndOwner;
+        UINT nFormat = 0;
+        GdkEvent *event;
+        GdkWindow *owner;
+
+        success = OpenClipboard (hwnd);
+        g_return_val_if_fail (success, 0);
+        hwndOwner = GetClipboardOwner ();
+        owner = gdk_win32_window_lookup_for_display (_gdk_display, hwndOwner);
+        if (owner == NULL)
+          owner = gdk_win32_window_foreign_new_for_display (_gdk_display, hwndOwner);
+
+        GDK_NOTE (DND, g_print (" drawclipboard owner: %p", hwndOwner));
+
+        if (_gdk_debug_flags & GDK_DEBUG_DND)
+          {
+            while ((nFormat = EnumClipboardFormats (nFormat)) != 0)
+              g_print ("%s ", _gdk_win32_cf_to_string (nFormat));
+          }
+
+        GDK_NOTE (DND, g_print (" \n"));
+
+        event = gdk_event_new (GDK_OWNER_CHANGE);
+        event->owner_change.window = _gdk_root;
+        event->owner_change.owner = owner;
+        event->owner_change.reason = GDK_OWNER_CHANGE_NEW_OWNER;
+        event->owner_change.selection = GDK_SELECTION_CLIPBOARD;
+        event->owner_change.time = _gdk_win32_get_next_tick (0);
+        event->owner_change.selection_time = GDK_CURRENT_TIME;
+        _gdk_win32_append_event (event);
+
+        CloseClipboard ();
+
+        if (_hwnd_next_viewer != NULL)
+          return SendMessage (_hwnd_next_viewer, message, wparam, lparam);
+
+        /* clear error to avoid confusing SetClipboardViewer() return */
+        SetLastError (0);
+        return 0;
+      }
+    default:
+      /* Otherwise call DefWindowProcW(). */
+      GDK_NOTE (EVENTS, g_print (" DefWindowProcW"));
+      return DefWindowProc (hwnd, message, wparam, lparam);
+    }
+}
+
+static LRESULT CALLBACK
+_clipboard_window_procedure (HWND   hwnd,
+                             UINT   message,
+                             WPARAM wparam,
+                             LPARAM lparam)
+{
+  LRESULT retval;
+
+  GDK_NOTE (EVENTS, g_print ("%s%*s%s %p",
+                            (debug_indent > 0 ? "\n" : ""),
+                            debug_indent, "",
+                            _gdk_win32_message_to_string (message), hwnd));
+  debug_indent += 2;
+  retval = inner_clipboard_window_procedure (hwnd, message, wparam, lparam);
+  debug_indent -= 2;
+
+  GDK_NOTE (EVENTS, g_print (" => %I64d%s", (gint64) retval, (debug_indent == 0 ? "\n" : "")));
+
+  return retval;
+}
+
+/*
+ * Creates a hidden window and adds it to the clipboard chain
+ */
+static HWND
+_gdk_win32_register_clipboard_notification (void)
+{
+  WNDCLASS wclass = { 0, };
+  HWND     hwnd;
+  ATOM     klass;
+
+  wclass.lpszClassName = "GdkClipboardNotification";
+  wclass.lpfnWndProc   = _clipboard_window_procedure;
+  wclass.hInstance     = _gdk_app_hmodule;
+
+  klass = RegisterClass (&wclass);
+  if (!klass)
+    return NULL;
+
+  hwnd = CreateWindow (MAKEINTRESOURCE (klass),
+                       NULL, WS_POPUP,
+                       0, 0, 0, 0, NULL, NULL,
+                       _gdk_app_hmodule, NULL);
+  if (!hwnd)
+    goto failed;
+
+  SetLastError (0);
+  _hwnd_next_viewer = SetClipboardViewer (hwnd);
+
+  if (_hwnd_next_viewer == NULL && GetLastError() != 0)
+    goto failed;
+
+  /* FIXME: http://msdn.microsoft.com/en-us/library/ms649033(v=VS.85).aspx */
+  /* This is only supported by Vista, and not yet by mingw64 */
+  /* if (AddClipboardFormatListener (hwnd) == FALSE) */
+  /*   goto failed; */
+
+  return hwnd;
+
+failed:
+  g_critical ("Failed to install clipboard viewer");
+  UnregisterClass (MAKEINTRESOURCE (klass), _gdk_app_hmodule);
+  return NULL;
 }
 
 static gboolean 
 gdk_win32_display_request_selection_notification (GdkDisplay *display,
-                                            GdkAtom     selection)
+                                                 GdkAtom     selection)
 
 {
-  return FALSE;
+  static HWND hwndViewer = NULL;
+  gboolean ret = FALSE;
+
+  GDK_NOTE (DND,
+            g_print ("gdk_display_request_selection_notification (..., %s)",
+                     gdk_atom_name (selection)));
+
+  if (selection == GDK_SELECTION_CLIPBOARD ||
+      selection == GDK_SELECTION_PRIMARY)
+    {
+      if (!hwndViewer)
+        {
+          hwndViewer = _gdk_win32_register_clipboard_notification ();
+          GDK_NOTE (DND, g_print (" registered"));
+        }
+      ret = (hwndViewer != NULL);
+    }
+  else
+    {
+      GDK_NOTE (DND, g_print (" unsupported"));
+      ret = FALSE;
+    }
+
+  GDK_NOTE (DND, g_print (" -> %s\n", ret ? "TRUE" : "FALSE"));
+  return ret;
 }
 
 static gboolean
index 3e98315e32003f830f261fc3cf5452d7459dfad7..cc19f962c15454f9c17c8eeb8c4918bc6adba7d7 100644 (file)
@@ -100,7 +100,6 @@ static gboolean gdk_event_dispatch (GSource     *source,
                                    GSourceFunc  callback,
                                    gpointer     user_data);
 
-static void append_event (GdkEvent *event);
 static gboolean is_modally_blocked (GdkWindow   *window);
 
 /* Private variable declarations
@@ -206,7 +205,7 @@ generate_focus_event (GdkDeviceManager *device_manager,
   event->focus_change.in = in;
   gdk_event_set_device (event, device);
 
-  append_event (event);
+  _gdk_win32_append_event (event);
 }
 
 static void
@@ -230,7 +229,7 @@ generate_grab_broken_event (GdkDeviceManager *device_manager,
   event->grab_broken.grab_window = grab_window;
   gdk_event_set_device (event, device);
 
-  append_event (event);
+  _gdk_win32_append_event (event);
 }
 
 static LRESULT 
@@ -895,8 +894,8 @@ fixup_event (GdkEvent *event)
   event->any.send_event = InSendMessage (); 
 }
 
-static void
-append_event (GdkEvent *event)
+void
+_gdk_win32_append_event (GdkEvent *event)
 {
   GList *link;
   
@@ -1164,13 +1163,13 @@ synthesize_enter_or_leave_event (GdkWindow        *window,
   event->crossing.state = 0;   /* FIXME: Set correctly */
   gdk_event_set_device (event, _gdk_display->core_pointer);
 
-  append_event (event);
+  _gdk_win32_append_event (event);
   
   if (type == GDK_ENTER_NOTIFY &&
       window->extension_events != 0)
     _gdk_device_wintab_update_window_coords (window);
 }
-                        
+
 /* The check_extended flag controls whether to check if the windows want
  * events from extended input devices and if the message should be skipped
  * because an extended input device is active
@@ -1329,7 +1328,7 @@ handle_configure_event (MSG       *msg,
       event->configure.x = point.x;
       event->configure.y = point.y;
 
-      append_event (event);
+      _gdk_win32_append_event (event);
     }
 }
 
@@ -1547,7 +1546,7 @@ generate_button_event (GdkEventType      type,
   event->button.button = button;
   gdk_event_set_device (event, _gdk_display->core_pointer);
 
-  append_event (event);
+  _gdk_win32_append_event (event);
 }
 
 static void
@@ -1982,7 +1981,7 @@ gdk_event_translate (MSG  *msg,
       if (msg->wParam == VK_MENU)
        event->key.state &= ~GDK_MOD1_MASK;
 
-      append_event (event);
+      _gdk_win32_append_event (event);
 
       return_val = TRUE;
       break;
@@ -2057,7 +2056,7 @@ gdk_event_translate (MSG  *msg,
         gdk_event_set_device (event, GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard);
              build_wm_ime_composition_event (event, msg, wbuf[i], key_state);
 
-             append_event (event);
+             _gdk_win32_append_event (event);
            }
          
          if (window->event_mask & GDK_KEY_RELEASE_MASK)
@@ -2068,7 +2067,7 @@ gdk_event_translate (MSG  *msg,
         gdk_event_set_device (event, GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard);
              build_wm_ime_composition_event (event, msg, wbuf[i], key_state);
 
-             append_event (event);
+             _gdk_win32_append_event (event);
            }
        }
       return_val = TRUE;
@@ -2193,7 +2192,7 @@ gdk_event_translate (MSG  *msg,
       event->motion.is_hint = FALSE;
       gdk_event_set_device (event, _gdk_display->core_pointer);
 
-      append_event (event);
+      _gdk_win32_append_event (event);
 
       return_val = TRUE;
       break;
@@ -2264,7 +2263,7 @@ gdk_event_translate (MSG  *msg,
       event->scroll.state = build_pointer_event_state (msg);
       gdk_event_set_device (event, _gdk_display->core_pointer);
 
-      append_event (event);
+      _gdk_win32_append_event (event);
       
       return_val = TRUE;
       break;
@@ -2424,7 +2423,7 @@ gdk_event_translate (MSG  *msg,
       event = gdk_event_new (msg->wParam ? GDK_MAP : GDK_UNMAP);
       event->any.window = window;
 
-      append_event (event);
+      _gdk_win32_append_event (event);
 
       if (event->any.type == GDK_UNMAP)
        {
@@ -2894,7 +2893,7 @@ gdk_event_translate (MSG  *msg,
       event = gdk_event_new (GDK_DELETE);
       event->any.window = window;
 
-      append_event (event);
+      _gdk_win32_append_event (event);
 
       impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
 
@@ -2926,7 +2925,7 @@ gdk_event_translate (MSG  *msg,
       event = gdk_event_new (GDK_DESTROY);
       event->any.window = window;
 
-      append_event (event);
+      _gdk_win32_append_event (event);
 
       return_val = TRUE;
       break;
@@ -2942,7 +2941,7 @@ gdk_event_translate (MSG  *msg,
          event->selection.window = window;
          event->selection.selection = GDK_SELECTION_CLIPBOARD;
          event->selection.time = _gdk_win32_get_next_tick (msg->time);
-          append_event (event);
+          _gdk_win32_append_event (event);
        }
       else
        {
@@ -2962,7 +2961,7 @@ gdk_event_translate (MSG  *msg,
        }
 
       /* We need to render to clipboard immediately, don't call
-       * append_event()
+       * _gdk_win32_append_event()
        */
       event = gdk_event_new (GDK_SELECTION_REQUEST);
       event->selection.window = window;
@@ -3075,7 +3074,7 @@ gdk_event_translate (MSG  *msg,
       g_object_ref (window);
 
       if (_gdk_input_other_event (event, msg, window))
-       append_event (event);
+       _gdk_win32_append_event (event);
       else
        gdk_event_free (event);
 
index 88d9f87afbf16dc89795097362b72fbede302f9d..2f320f15b0c2558f7f6a9b740bdff119e242ea70 100644 (file)
@@ -493,6 +493,7 @@ GdkAtom _gdk_win32_display_manager_atom_intern (GdkDisplayManager *manager,
                                                gint         only_if_exists);
 gchar *_gdk_win32_display_manager_get_atom_name (GdkDisplayManager *manager, 
                                                 GdkAtom            atom);
+void _gdk_win32_append_event (GdkEvent *event);
 
 /* Initialization */
 void _gdk_win32_windowing_init (void);